<?php
//
//	file: sys/form.class.php
//	author: Pierre ROGE
//	begin: 05/12/2007
//	version: 0.0.2.CH - 18/08/2008
//	license: http://opensource.org/licenses/gpl-license.php GNU General Public License (GPL)
//

if ( !defined('SYS') )
{
	die('Not allowed');
}

class sys_form extends sys_stdclass
{
	var $fields;
	var $struct;
	var $buttons;
	var $jscripts;
	var $submited;
	var $validated;

	var $tpls;

	function __construct($api_name)
	{
		parent::__construct($api_name);
		$this->fields = false;
		$this->struct = false;
		$this->buttons = false;
		$this->jscripts = false;
		$this->submited = false;
		$this->validated = false;
	}

	function __destruct()
	{
		unset($this->validate);
		unset($this->submited);
		unset($this->buttons);
		unset($this->struct);
		if ( $this->fields )
		{
			$keys = array_keys($this->fields);
			foreach ( $keys as $key )
			{
				sys::kill($this->fields[$key]);
				unset($this->fields[$key]);
			}
		}
		unset($this->fields);
		parent::__destruct();
	}

	function set(&$fields, $buttons=false, $jscripts=false)
	{
		$sys = &$GLOBALS[SYS];
		$this->tpls = array();

		if ( !$this->struct )
		{
			$this->struct = array();
		}
		if ( !$this->fields )
		{
			$this->fields = array();
		}
		if ( $fields )
		{
			$sys->ini_get('form.fields', 'class');

			// block level
			$block_names = array_keys($fields);
			foreach ( $block_names as $block_name )
			{
				if ( !isset($fields[$block_name]['fields']) )
				{
					trigger_error(sys_error::sprintf('err_form_block_missing', $block_name), E_USER_ERROR);
				}
				$fields[$block_name]['type'] = isset($fields[$block_name]['type']) ? $fields[$block_name]['type'] : 'fieldset';
				if ( !isset($fields[$block_name]['legend']) && ($fields[$block_name]['type'] == 'fieldset') )
				{
					$fields[$block_name]['type'] = 'div';
				}
				if ( isset($fields[$blockname]['tpls']) )
				{
					if ( $fields[$blockname]['tpls'] )
					{
						$tpls = is_array($fields[$blockname]['tpls']) ? $fields[$blockname]['tpls'] : array($fields[$blockname]['tpls']);
						foreach ( $tpls as $tpl )
						{
							$this->tpls[$tpl] = true;
						}
					}
					unset($fields[$blockname]['tpls']);
				}
				if ( !isset($this->struct[$block_name]) )
				{
					$this->struct[$block_name] = array();
				}
				$this->struct[$block_name] += $fields[$block_name] + array('fields' => array());

				// fields level
				$field_names = array_keys($fields[$block_name]['fields']);
				foreach ( $field_names as $field_name )
				{
					// field
					if ( !isset($fields[$block_name]['fields'][$field_name]['fields']) )
					{
						$this->set_field($fields[$block_name]['fields'][$field_name], $field_name);
						$this->struct[$block_name]['fields'][$field_name] = true;
					}
					// fields group
					else
					{
						// nb.: we don't handle sub-sub fields (ie a splited date combined to other fields)
						// this must be done within a specific field class def
						if ( !isset($this->struct[$block_name]['fields'][$field_name]) )
						{
							$this->struct[$block_name]['fields'][$field_name]['fields'] = array();
						}
						if ( isset($fields[$block_name]['fields'][$field_name]['tpls']) )
						{
							if ( $fields[$block_name]['fields'][$field_name]['tpls'] )
							{
								$tpls = is_array($fields[$block_name]['fields'][$field_name]['tpls']) ? $fields[$block_name]['fields'][$field_name]['tpls'] : array($fields[$block_name]['fields'][$field_name]['tpls']);
								foreach ( $tpls as $tpl )
								{
									$this->tpls[$tpl] = true;
								}
							}
							unset($fields[$block_name]['fields'][$field_name]['tpls']);
						}

						$this->struct[$block_name]['fields'][$field_name] += $fields[$block_name]['fields'][$field_name] + array('fields' => array());
						$sub_names = array_keys($fields[$block_name]['fields'][$field_name]['fields']);
						foreach ( $sub_names as $sub_name )
						{
							if ( $sub_name == $field_name )
							{
								$this->struct[$block_name]['fields'][$field_name]['is_field_id'] = true;
							}
							// legend is not defined at group level: get it from the first field
							if ( isset($fields[$block_name]['fields'][$field_name]['fields'][$sub_name]['legend']) && !isset($this->struct[$block_name]['fields'][$field_name]['legend']) )
							{
								$this->struct[$block_name]['fields'][$field_name]['legend'] = $fields[$block_name]['fields'][$field_name]['fields'][$sub_name]['legend'];
								unset($fields[$block_name]['fields'][$field_name]['fields'][$sub_name]['legend']);
								foreach ( $fields[$block_name]['fields'][$field_name]['fields'][$sub_name] as $attr => $value )
								{
									if ( preg_match('#^legend\.#i', $attr) )
									{
										$this->struct[$block_name]['fields'][$field_name][$attr] = $fields[$block_name]['fields'][$field_name]['fields'][$sub_name][$attr];
										unset($fields[$block_name]['fields'][$field_name]['fields'][$sub_name][$attr]);
									}
								}
							}
							$this->set_field($fields[$block_name]['fields'][$field_name]['fields'][$sub_name], $sub_name);
							$this->struct[$block_name]['fields'][$field_name]['fields'][$sub_name] = true;
						}
					}
				}
			}
		}

		// add buttons to the form
		if ( $buttons )
		{
			$this->set_buttons($buttons);
		}

		// add jscripts to the page
		if ( $jscripts )
		{
			$GLOBALS[$this->api_name]->set_resource('jscripts', $jscripts);
		}
	}

	function set_buttons($buttons)
	{
		if ( empty($buttons) )
		{
			return false;
		}
		$this->buttons = $this->buttons ? array_merge($this->buttons, $buttons) : $buttons;
		return true;
	}

	function set_field(&$def, $name)
	{
		$sys = &$GLOBALS[SYS];

		if ( isset($this->fields[$name]) )
		{
			trigger_error(sys_error::sprintf('err_form_field_defined', $name), E_USER_ERROR);
		}

		$def['type'] = isset($def['type']) ? $def['type'] : 'varchar';
		$def['layer'] = isset($def['layer']) ? $def['layer'] : false;
		$type = isset($def['type']) && $def['type'] ? $def['type'] : 'varchar';
		$layer = isset($def['layer']) ? $def['layer'] : false;
		unset($def['type'], $def['layer']);

		if ( isset($def['tpls']) )
		{
			if ( $def['tpls'] )
			{
				$tpls = is_array($def['tpls']) ? $def['tpls'] : array($def['tpls']);
				foreach ( $tpls as $tpl )
				{
					$this->tpls[$tpl] = true;
				}
			}
			unset($def['tpls']);
		}

		// get the class
		$class = 'sys_field_' . $type;
		if ( ($error = !class_exists($class)) && $layer && ($filename = sys::realpath($sys->root . $layer . $sys->ext)) )
		{
			include($sys->root . $layer . $sys->ext);
			if ( !($error = !class_exists($class)) )
			{
				$sys->lang->register($layer);
			}
		}
		if ( $error )
		{
			trigger_error(sys_error::sprintf('err_form_field_class_missing', $name), E_USER_ERROR);
		}

		// instantiate the field and set the def
		$this->fields[$name] = new $class($this->api_name);
		$this->fields[$name]->set($name, $type, $def);
	}

	function process($tpl_switch=false)
	{
		if ( $this->init() )
		{
			$this->check();
			$this->validate();
			return $this->display($tpl_switch);
		}
		return false;
	}

	function init()
	{
		$sys = &$GLOBALS[SYS];
		$io = &$sys->io;

		// check buttons
		if ( $this->buttons )
		{
			foreach ( $this->buttons as $name => $def )
			{
				if ( $io->button($name) )
				{
					if ( ($name == 'cancel_form') || (isset($def['cancel']) && $def['cancel']) )
					{
						return false;
					}
					$this->submited = true;
					$this->validated = ($name == 'submit_form') || (isset($def['submit']) && $def['submit']);
				}
			}
		}
		if ( empty($this->fields) )
		{
			trigger_error('err_form_no_fields', E_USER_ERROR);
		}
		return true;
	}

	function check()
	{
		if ( $this->submited )
		{
			$keys = array_keys($this->fields);
			foreach ( $keys as $key )
			{
				$this->fields[$key]->check();
			}
		}
	}

	function validate()
	{
		$sys = &$GLOBALS[SYS];
		$error = &$sys->error;

		if ( !$error->warnings && $this->validated )
		{
			$keys = array_keys($this->fields);
			foreach ( $keys as $key )
			{
				$this->fields[$key]->validate();
			}
		}
		if ( $error->warnings )
		{
			$this->validated = false;
		}
	}

	function display($tpl_switch=false)
	{
		$sys = &$GLOBALS[SYS];
		$tpl = &$sys->tpl;

		$switch = $tpl_switch && ($tpl_switch = trim($tpl_switch)) ? $tpl_switch . '.' : '';

		if ( $this->validated )
		{
			return false;
		}
		if ( $this->tpls )
		{
			foreach ( $this->tpls as $file => $dummy )
			{
				$tpl->add($switch . 'fields_def', array(
					'TPL' => $file,
				));
			}
		}
		foreach ( $this->struct as $block_name => $block_def )
		{
			// blocks
			$tpl_data = array('NAME' => $block_name);
			foreach ( $block_def as $key => $value )
			{
				if ( $key != 'fields' )
				{
					$tpl_data[ sys_field::_tpl_varname($key) ] = $value;
				}
			}
			$tpl->add($switch . 'block', $tpl_data);
			unset($tpl_data);

			// lines
			foreach ( $this->struct[$block_name]['fields'] as $field_name => $field_def )
			{
				// single field
				if ( !isset($field_def['fields']) )
				{
					$tpl->add($switch . 'block.line');
					$this->fields[$field_name]->display($tpl_switch);
				}
				// fields group
				else
				{
					// send the group header
					$tpl_data = array('NAME' => $field_name);
					foreach ( $field_def as $key => $value )
					{
						if ( $key != 'fields' )
						{
							$tpl_data[ sys_field::_tpl_varname($key) ] = $value;
						}
					}
					$tpl->add($switch . 'block.line', $tpl_data);
					$tpl->add($switch . 'block.line.group');
					unset($tpl_data);
					foreach ( $this->struct[$block_name]['fields'][$field_name]['fields'] as $sub_name => $field_def )
					{
						$this->fields[$sub_name]->display($tpl_switch);
					}
				}
			}
		}
		if ( $this->buttons )
		{
			foreach ( $this->buttons as $name => $def )
			{
				unset($def);
				$tpl_data = array(
					'NAME' => $name,
				);
				if ( $this->buttons[$name] )
				{
					foreach ( $this->buttons[$name] as $attr => $value )
					{
						$tpl_data[ sys_field::_tpl_varname($attr) ] = $value;
					}
				}
				$tpl->add($switch . 'button', $tpl_data);
			}
		}
		return true;
	}
}

?>